package main

import (
	"fmt"
	"runtime"
)

func main() {

	fmt.Println("start")
	if err := proposal(); err != nil {
		fmt.Println(fmt.Sprintf("Error occurred:%v", err))
		return
	}
	fmt.Sprintf("finished")
}

func proposal() (err error) {
	fmt.Println("panicHandle")
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("create cart order panic:%v", r) //错误封装，对调用方暴露panic信息
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			fmt.Println(fmt.Sprintf("create cart order panic. recover:%v,stack:%v.",
				r, string(buf[:n]))) //记录堆栈细节，记录到日志，以便问题定位
		}
	}()
	//err = fmt.Errorf("e0")
	err = do()
	return err
}

// panic 被隐藏，调用方会认为执行成功
func badCase1() error {
	fmt.Println("present")
	defer func() {
		if r := recover(); r != nil { // 处理 panic 异常，事务回滚
			//tx.Rollback()
			fmt.Println("aaaaaa")
		}
		//cancel()
	}()
	err := do()
	return err
}

// 错误，error 无法传递给调用方
func badCase2() error {
	fmt.Println("panicHandle2")
	var err error
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("create cart order panic:%v", r)
			var buf [4096]byte
			n := runtime.Stack(buf[:], false)
			fmt.Println(fmt.Sprintf("create cart order panic. recover:%v,stack:%v.", r, string(buf[:n])))
		}
	}()
	//err = fmt.Errorf("e1")
	err = do()
	return err
}

func do() error {
	panic("ppp")
	// 对 panic 的理解：panic 致命的/灾难性的错误，意味着代码不能继续运行, fatal error（就是挂了）
	// 处理规则：不能假设调用者来解决 panic
	// 引起 panic 的原因：1.空指针引用；2.数组越界；3.空接口类型断言失败；4.显示调用

	//1.空指针
	var ptr *int
	fmt.Println(*ptr) // 会引发panic

	//2. 数组越界
	//arr := [3]int{1, 2, 3}   //编译器可识别越界
	var arr []int
	arr = append(arr, 1, 2, 3)
	fmt.Println(arr[5]) // 会引发panic

	//3. 空接口类型断言失败
	var i interface{} = 42
	fmt.Println(i.(string)) // 会引发panic，因为i的具体类型是int而不是string

	//4. 除以零
	x := 10
	y := 0
	fmt.Println(x / y)

	//5. 显示调用
	// 对于真正意外的情况，那些表示不可恢复的程序错误，例如索引越界、不可恢复的环境问题、栈溢出，我们才使用 panic。
	// 对于其他的错误情况，应该使用 error 来进行判定。
	panic("Something went wrong!")

	//6. 未捕获的异常：某些情况下，操作系统错误或运行时错误，Go语言运行时会引发未捕获的异常，导致程序崩溃。

}
